﻿using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.Collections.Generic;
using System.Windows.Input;
using TeamExplorer.Merge.Base;
using System.Linq;
using System.Reflection;
using System;
using System.Diagnostics;

namespace TeamExplorer.Merge.MergeCandidates
{
    public class BranchListViewModel : NotifyPropertyChangedBase
    {
        private string _systemDefaultSourceBranch;
        private string _systemDefaultTargetBranch;
        private string _sourceBranch;
        private string _targetBranch;
        private BranchObject _sourceBranchObject;
        private BranchObject _targetBranchObject;

        private List<BranchObject> _sourceBranches, _targetBranches;

        private VersionControlServer vcs;
        private bool _branchesAreDirty;

        public ICommand SelectSourceBranchCommand { get; private set; }
        public ICommand SelectTargetBranchCommand { get; private set; }
        public ICommand SaveDefaultBranchesCommand { get; private set; }

        public BranchListViewModel(VersionControlServer vcs)
        {
            this.vcs = vcs;
            SetUpCommands();
        }

        public void Init()
        {
            PopulateBranchLists();
            PopulateFromSystemDefaults();
        }

        private void PopulateFromSystemDefaults()
        {
            _systemDefaultSourceBranch = Environment.GetEnvironmentVariable("AutoAssociateDefaultSourceBranch", EnvironmentVariableTarget.User);
            _systemDefaultTargetBranch = Environment.GetEnvironmentVariable("AutoAssociateDefaultTargetBranch", EnvironmentVariableTarget.User);

            if (_systemDefaultSourceBranch == null || _systemDefaultTargetBranch == null || SourceBranches == null || TargetBranches == null) return;

            var sb = SourceBranches.FirstOrDefault(s => s.Properties.RootItem.Item.Equals(_systemDefaultSourceBranch));
            SetSourceBranch(sb);
            var tb = TargetBranches.FirstOrDefault(t => t.Properties.RootItem.Item.Equals(_systemDefaultTargetBranch));
            SetTargetBranch(tb);
            _branchesAreDirty = sb == null || tb == null;
        }

        private void SetUpCommands()
        {
            SelectSourceBranchCommand = new DelegateCommand(ExecuteSelectSourceBranch);
            SelectTargetBranchCommand = new DelegateCommand(ExecuteSelectTargetBranch);
            SaveDefaultBranchesCommand = new DelegateCommand(ExecuteSaveBranches, CanExecuteSaveBranches);
        }

        private void ExecuteSelectSourceBranch(object branchParam)
        {
            BranchObject s = branchParam as BranchObject;            
            SetSourceBranch(s);
        }

        private void SetSourceBranch(BranchObject s)
        {
            if (s == null) return;
            _sourceBranchObject = s;
            SourceBranch = s.Properties.RootItem.Item;
        }

        private void ExecuteSelectTargetBranch(object branchParam)
        {
            BranchObject t = branchParam as BranchObject;            
            SetTargetBranch(t);
        }

        private void SetTargetBranch(BranchObject t)
        {
            if (t == null) return;
            _targetBranchObject = t;
            TargetBranch = t.Properties.RootItem.Item;
        }

        private void ExecuteSaveBranches(object obj)
        {
            SaveDefault();
            _branchesAreDirty = false;
        }

        private bool CanExecuteSaveBranches(object obj)
        {
            return _branchesAreDirty;
        }

        private void PopulateBranchLists()
        {
            SetSourceBranchProperties();
            SetTargetBranchProperties(_sourceBranchObject);
        }

        private void SetSourceBranchProperties()
        {
            List<BranchObject> possibleBranches = new List<BranchObject>();

            BranchObject[] branches = vcs.QueryRootBranchObjects(RecursionType.None);
            foreach (var rootBranch in branches)
            {
                possibleBranches.AddRange(GetAllBranches(rootBranch, true, RecursionType.Full));
            }
            if (!possibleBranches.Any())
            {
                SourceBranches = null;
                SourceBranch = null;
                TargetBranch = null;
                TargetBranches = null;
                return; //TODO: Display error message, disable merge candidates?
            }

            SourceBranches = possibleBranches;
            _sourceBranchObject = possibleBranches.First();
            SourceBranch = _sourceBranchObject.Properties.RootItem.Item;
        }

        private void SetTargetBranchProperties(BranchObject source)
        {
            List<BranchObject> possibleTargetBranches = new List<BranchObject>();
            possibleTargetBranches.AddRange(GetAllBranches(source, false, RecursionType.OneLevel));

            //add parent as target in addition to any child branches
            if (source.Properties.ParentBranch != null)
            {
                possibleTargetBranches.Add(SourceBranches.Single(sb => sb.Properties.RootItem.Equals(source.Properties.ParentBranch))); //can assume using the source list as merging is happen
            }

            if (!possibleTargetBranches.Any())
            {
                TargetBranches = null;
                TargetBranch = null;
                return; //TODO: Display message to choose a different source
            }
            TargetBranches = possibleTargetBranches;
            TargetBranch = possibleTargetBranches.First().Properties.RootItem.Item;
        }

        public string SourceBranch
        {
            get
            {
                return _sourceBranch;
            }
            set
            {
                if (_sourceBranch == value) return;
                _sourceBranch = value;
                _branchesAreDirty = _sourceBranch != _systemDefaultSourceBranch;
                SetTargetBranchProperties(_sourceBranchObject);
                RaisePropertyChanged("SourceBranch");
            }
        }

        public string TargetBranch
        {
            get
            {
                return _targetBranch;
            }
            set
            {
                if (_targetBranch == value) return;
                _targetBranch = value;
                _branchesAreDirty = _targetBranch != _systemDefaultTargetBranch;
                RaisePropertyChanged("TargetBranch");
            }
        }

        public List<BranchObject> SourceBranches
        {
            get
            {
                return _sourceBranches;
            }
            set
            {
                _sourceBranches = value;
                RaisePropertyChanged("SourceBranches");
            }
        }

        public List<BranchObject> TargetBranches
        {
            get
            {
                return _targetBranches;
            }
            set
            {
                _targetBranches = value;
                RaisePropertyChanged("TargetBranches");
            }
        }

        private IEnumerable<BranchObject> GetAllBranches(BranchObject bo, bool includeSelf, RecursionType recursionType)
        {
            BranchObject[] branches = vcs.QueryBranchObjects(bo.Properties.RootItem, recursionType);
            return branches.Where(b => 
                !b.Properties.RootItem.IsDeleted 
                && (includeSelf || !b.Properties.RootItem.Equals(bo.Properties.RootItem))
                && (b.ChildBranches.Length > 0 || b.Properties.ParentBranch != null));
        }

        private void SaveDefault()
        {
            try
            {
                if (String.IsNullOrEmpty(SourceBranch) || String.IsNullOrEmpty(TargetBranch)) return;

                Environment.SetEnvironmentVariable("AutoAssociateDefaultSourceBranch", SourceBranch, EnvironmentVariableTarget.User);
                Environment.SetEnvironmentVariable("AutoAssociateDefaultTargetBranch", TargetBranch, EnvironmentVariableTarget.User);

                _systemDefaultSourceBranch = SourceBranch;
                _systemDefaultTargetBranch = TargetBranch;
            }
            catch (SecurityException se)
            {
                Debug.WriteLine("Cannot save default source and target branch due to a security exception. " + se.ToString());
            }
        }
    }
}
